package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class SysLogAspect {
    private static final Logger log =
            LoggerFactory.getLogger(SysLogAspect.class);

    /**
     * @Pointcut 注解用于定义切入点
     * @annotation (注解)为切入点表达式，后续由此注解描述的方法为切入点方法
     */
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){
        System.out.println("切入点是干啥的呢");
    }

    /**
     * @Around 注解描述的方法,可以在切入点执行之前和之后进行业务拓展,
     * @param jp 连接点对象,此对象封装了要执行的目标方法信息.
     * 可以通过连接点对象调用目标方法.
     * @return 目标方法的执行结果
     * @throws Throwable
     */
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint jp) throws Throwable {
        long t1 = System.currentTimeMillis();
        try {
            Object result = jp.proceed();
            long t2 = System.currentTimeMillis();
            log.info("opertime:{}",t2-t1);
            doLogInfo(jp,t2-t1,null);
            return  result;
        } catch (Throwable e) {
            e.printStackTrace();
            long t2 = System.currentTimeMillis();
            log.info("exception:{}",t2-t1);
            doLogInfo(jp, t2-t1, e);
            throw e;
        }
    }

    @Autowired
    private SysLogService sysLogService;

    private void doLogInfo(ProceedingJoinPoint jp, long time, Throwable e) throws Throwable {
        //1.获取用户行为日志
        //1.1 获取登录用户名(没做登录时，可以先给个固定值)
        String username="cgb";
        //1.2 获取 ip 地址
        String ip= "202.106.0.20";
        //1.3 获取操作名(operation)-@RequiredLog 注解中 value 属性的值
        //1.3.1 获取目标对象类型
        Class<?> targetCls=jp.getTarget().getClass();
        //1.3.2 获取目标方法
        MethodSignature ms=
                (MethodSignature) jp.getSignature();//方法签名
        Method targetMethod=targetCls.getMethod(
                ms.getName(),ms.getParameterTypes());
        //1.3.3 获取方法上 RequiredLog 注解
        RequiredLog annotation =
                targetMethod.getAnnotation(RequiredLog.class);
        //1.3.4 获取注解中定义操作名
        String operation=annotation.operation();
        //1.4 获取方法声明(类全名+方法名)
        String classMethodName=
                targetCls.getName()+"."+targetMethod.getName();
        //1.5 获取方法实际参数信息
        Object[]args=jp.getArgs();
        String params=new ObjectMapper().writeValueAsString(args);
        //2.封装用户行为日志
        SysLog sysLog=new SysLog();
        sysLog.setUsername(username);
        sysLog.setIp(ip);
        sysLog.setOperation(operation);
        sysLog.setMethod(classMethodName);
        sysLog.setParams(params);
        sysLog.setTime(time);
        if(e!=null) {
            sysLog.setStatus(0);
            sysLog.setError(e.getMessage());
        }
        //3.打印日志
        String userLog=new ObjectMapper()
                .writeValueAsString(sysLog);
        log.info("user.oper {}",userLog);
        sysLogService.saveLog(sysLog);
    }


}
